﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Text;

namespace Srk.BetaseriesApi.Clients {

    /// <summary>
    /// This is a base partial Betaseries client implementation fitted for HTTP data transfert.
    /// This is to be subclassed and overriden.
    /// </summary>
    public partial class BetaseriesBaseHttpClient : IBetaseriesApi, IDisposable {

        #region Properties

        /// <summary>
        /// Base HTTP url for queries. 
        /// This will permit to use a different base adresse (for HTTPS, different port or domain name...).
        /// Default is http://api.betaseries.com/.
        /// </summary>
        /// <remarks>
        /// Value must be setted from .ctor.
        /// </remarks>
        protected readonly string BaseUrl;

        /// <summary>
        /// Formating string for query string.
        /// Must be set from sub-class.
        /// </summary>
        protected readonly string UrlFormat;

#pragma warning disable 1591
        #region IBetaseriesBaseApi Members

        public string UserAgent {
            get { return _userAgent; }
            set { _userAgent = value; }
        }
        private string _userAgent;

        public string Key {
            get { return _key; }
            set { _key = value; }
        }
        private string _key;

        public string SessionToken {
            get { return _sessionToken; }
        }
        private string _sessionToken;

        public string SessionUsername {
            get { return _sessionUsername; }
        }
        private string _sessionUsername;

        public void SetSessionTokens(string newSessionToken, string newSessionUsername) {
            _sessionToken = newSessionToken;
            _sessionUsername = newSessionUsername;

            if (factory != null) {
                factory.RaiseSessionTokenChanged(this, _sessionToken, _sessionUsername);
            }
        }

        #endregion
#pragma warning restore 1591

        #endregion

        #region .ctor

        /// <summary>
        /// Protected class .ctor.
        /// </summary>
        /// <param name="urlFormat">is something like "{0}{1}.xml?{2}" (0 is the BaseUrl, 1 is the action, 2 is for query parameters)</param>
        /// <param name="apiKey">your API key (ask it on the website, don't use someone else's)</param>
        protected BetaseriesBaseHttpClient(string urlFormat, string apiKey) : this(urlFormat, apiKey, null, null) { }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="urlFormat">is something like "{0}{1}.xml?{2}" (0 is the BaseUrl, 1 is the action, 2 is for query parameters)</param>
        /// <param name="apiKey">your API key (ask it on the website, don't use someone else's)</param>
        /// <param name="userAgent">anything like MyBetaseriesApp/1.0.0.0 (name/version)</param>
        protected BetaseriesBaseHttpClient(string urlFormat, string apiKey, string userAgent) : this(urlFormat, apiKey, userAgent, null) { }

        /// <summary>
        /// Mandatory .ctor
        /// </summary>
        /// <param name="urlFormat">is something like "{0}{1}.xml?{2}" (0 is the BaseUrl, 1 is the action, 2 is for query parameters)</param>
        /// <param name="apiKey">your API key (ask it on the website, don't use someone else's)</param>
        /// <param name="userAgent">anything like MyBetaseriesApp/1.0.0.0 (name/version)</param>
        /// <param name="baseUrl">If you want to override urls (nice for https)</param>
        protected BetaseriesBaseHttpClient(string urlFormat, string apiKey, string userAgent, string baseUrl) {
            if (string.IsNullOrEmpty(apiKey))
                throw new ArgumentException("Missing API Key", "apiKey");
            Key = apiKey;
            UserAgent = userAgent ?? "unknown-client";
            BaseUrl = baseUrl ?? "http://api.betaseries.com/";
            UrlFormat = urlFormat;
        }

        #endregion

        #region Event raising

        /// <summary>
        /// Raise an event.
        /// Does not work. See http://stackoverflow.com/questions/4344628/how-to-raise-parent-class-events-with-expression-c.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="func"></param>
        /// <param name="eventArgs"></param>
        protected void Raise<T>(Func<BetaseriesBaseHttpClient, AsyncResponseHandler<T>> func, AsyncResponseArgs<T> eventArgs) {
            AsyncResponseHandler<T> handler = func(this);
            if (!RaiseCheck(handler))
                return;
            handler(this, eventArgs);
        }

        /// <summary>
        /// Raise an event.
        /// Does not work. See http://stackoverflow.com/questions/4344628/how-to-raise-parent-class-events-with-expression-c.
        /// </summary>
        /// <param name="func"></param>
        /// <param name="eventArgs"></param>
        protected void Raise(Func<BetaseriesBaseHttpClient, AsyncResponseHandler> func, AsyncResponseArgs eventArgs) {
            AsyncResponseHandler handler = func(this);
            if (!RaiseCheck(handler))
                return;
            handler(this, eventArgs);
        }

        /// <summary>
        /// Raise an event.
        /// Does not work. See http://stackoverflow.com/questions/4344628/how-to-raise-parent-class-events-with-expression-c.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="func"></param>
        /// <param name="eventArgs"></param>
        [Obsolete]
        protected void Raise<T>(Func<BetaseriesBaseHttpClient, EventHandler<T>> func, T eventArgs)
    where T : EventArgs {
            EventHandler<T> handler = func(this);
            //if (!RaiseCheck(handler))
            //    return;
            handler(this, eventArgs);
        }

        /// <summary>
        /// Raise an event.
        /// </summary>
        /// <param name="handler"></param>
        /// <param name="args"></param>
        protected void Raise(AsyncResponseHandler handler, AsyncResponseArgs args) {
            if (!RaiseCheck(handler))
                return;
            handler(this, args);
        }

        /// <summary>
        /// Raise an event.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="handler"></param>
        /// <param name="args"></param>
        protected void Raise<T>(AsyncResponseHandler<T> handler, AsyncResponseArgs<T> args) {
            if (!RaiseCheck(handler))
                return;
            handler(this, args);
        }

        private bool RaiseCheck(AsyncResponseHandler handler) {
            if (handler != null)
                return true;
            System.Diagnostics.Debug.WriteLine("Received a server response but the associated event handler in BetaseriesBaseHttpClient is null.");
            return false;
        }

        private bool RaiseCheck<T>(AsyncResponseHandler<T> handler) {
            if (handler != null)
                return true;
            System.Diagnostics.Debug.WriteLine("Received a server response but the associated event handler in BetaseriesBaseHttpClient is null.");
            return false;
        }

        #endregion

        #region HTTP Query

        /// <summary>
        /// Execute an HTTP GET request through an HTTP wrapper.
        /// </summary>
        /// <param name="callback">delegate called when the query is succesful</param>
        /// <param name="errorCallback">delegate called when the query encoutered an error</param>
        /// <param name="action">Service action</param>
        /// <param name="keyValues">Pairs of query string parameters (key1, value1, key2, value2...)</param>
        /// <returns>HTTP response body as a string.</returns>
        protected virtual void ExecuteQuery(AsyncCallback callback, Action<Exception> errorCallback, string action, params string[] keyValues) {
            ExecuteQuery(callback, errorCallback, action, null, keyValues);
        }

        /// <summary>
        /// Execute an HTTP GET request through an HTTP wrapper.
        /// </summary>
        /// <param name="callback">delegate called when the query is succesful</param>
        /// <param name="errorCallback">delegate called when the query encoutered an error</param>
        /// <param name="action">Service action</param>
        /// <param name="parameters">Query string parameters</param>
        protected virtual void ExecuteQuery(AsyncCallback callback, Action<Exception> errorCallback, string action, Dictionary<string, string> parameters) {
            ExecuteQuery(callback, errorCallback, action, null, parameters);
        }

        /// <summary>
        /// Execute an HTTP GET/POST request through an HTTP wrapper.
        /// Request will use a POST method if postParameters contains stuff.
        /// </summary>
        /// <param name="callback">delegate called when the query is succesful</param>
        /// <param name="errorCallback">delegate called when the query encoutered an error</param>
        /// <param name="action">Service action</param>
        /// <param name="postParameters">POST data parameters (pass null for GET method)</param>
        /// <param name="keyValues">Pairs of query string parameters (key1, value1, key2, value2...)</param>
        /// <returns>HTTP response body as a string.</returns>
        protected virtual void ExecuteQuery(AsyncCallback callback, Action<Exception> errorCallback, string action, Dictionary<string, string> postParameters, params string[] keyValues) {
            if (keyValues.Length % 2 != 0)
                throw new ArgumentException("Invalid parameters count", "keyvalues");
            Dictionary<string, string> parameters = null;
            if (keyValues.Length > 0) {
                parameters = new Dictionary<string, string>();
                bool isKey = true;
                string key = null;
                foreach (var item in keyValues) {
                    if (isKey) {
                        key = item;
                    } else {
                        parameters.Add(key, item);
                    }
                    isKey = !isKey;
                }
            }
            ExecuteQuery(callback, errorCallback, action, postParameters, parameters);
        }

        /// <summary>
        /// Execute an HTTP GET/POST request through an HTTP wrapper.
        /// Request will use a POST method if postParameters contains stuff.
        /// </summary>
        /// <param name="callback">delegate called when the query is succesful</param>
        /// <param name="errorCallback">delegate called when the query encoutered an error</param>
        /// <param name="action">Service action</param>
        /// <param name="postParameters">POST data parameters (pass null for GET method)</param>
        /// <param name="parameters">Query string parameters</param>
        protected virtual void ExecuteQuery(AsyncCallback callback, Action<Exception> errorCallback, string action, Dictionary<string, string> postParameters, Dictionary<string, string> parameters) {
            parameters = parameters ?? new Dictionary<string, string>();
            parameters["key"] = Key;
            if (SessionToken != null) {
                parameters["token"] = SessionToken;
            }

            if (postParameters != null && postParameters.Count > 0)
                http.ExecuteQuery(callback, errorCallback, action, parameters, postParameters);
            else
                http.ExecuteQuery(callback, errorCallback, action, parameters);
        }

        #endregion

#pragma warning disable 1591
        #region IBetaseriesApi Members

        public virtual void SearchShowsAsync(string title) {
            throw new NotSupportedException();
        }

        protected AsyncResponseHandler<IList<Show>> _searchShowsEnded;
        public event AsyncResponseHandler<IList<Show>> SearchShowsEnded {
            add { _searchShowsEnded += value; }
            remove { _searchShowsEnded -= value; }
        }

        public virtual void GetShowAsync(string url) {
            throw new NotSupportedException();
        }

        public event AsyncResponseHandler<Show> GetShowEnded {
            add { _getShowEnded += value; }
            remove { _getShowEnded -= value; }
        }
        protected AsyncResponseHandler<Show> _getShowEnded;

        public virtual void GetShowsAsync() {
            throw new NotSupportedException();
        }

        public event AsyncResponseHandler<IList<Show>> GetShowsEnded {
            add { _getShowsEnded += value; }
            remove { _getShowsEnded -= value; }
        }
        protected AsyncResponseHandler<IList<Show>> _getShowsEnded;

        public virtual void GetEpisodesAsync(string showUrl) {
            throw new NotSupportedException();
        }

        public virtual void GetEpisodesAsync(string showUrl, uint season) {
            throw new NotSupportedException();
        }

        public event AsyncResponseHandler<IList<Episode>> GetEpisodesEnded {
            add { _getEpisodesEnded += value; }
            remove { _getEpisodesEnded -= value; }
        }
        protected AsyncResponseHandler<IList<Episode>> _getEpisodesEnded;

        public virtual void GetEpisodeAsync(string showUrl, uint season, uint episode) {
            throw new NotSupportedException();
        }

        public event AsyncResponseHandler<Episode> GetEpisodeEnded {
            add { _getEpisodeEnded += value; }
            remove { _getEpisodeEnded -= value; }
        }
        protected AsyncResponseHandler<Episode> _getEpisodeEnded;
        

        public virtual void AddShowAsync(string showUrl) {
            throw new NotSupportedException();
        }

        public event AsyncResponseHandler AddShowEnded {
            add { _addShowEnded += value; }
            remove { _addShowEnded -= value; }
        }
        protected AsyncResponseHandler _addShowEnded;

        public virtual void RemoveShowAsync(string showUrl) {
            throw new NotSupportedException();
        }

        public event AsyncResponseHandler RemoveShowEnded {
            add { _removeShowEnded += value; }
            remove { _removeShowEnded -= value; }
        }
        protected AsyncResponseHandler _removeShowEnded;
        
        public virtual void GetShowSubtitlesAsync(string showUrl, string language, uint? season, uint? episode) {
            throw new NotSupportedException();
        }

        public event AsyncResponseHandler<IList<Subtitle>> GetShowSubtitlesEnded {
            add { _getShowSubtitlesEnded += value; }
            remove { _getShowSubtitlesEnded -= value; }
        }
        protected AsyncResponseHandler<IList<Subtitle>> _getShowSubtitlesEnded;
        

        public virtual void GetLatestSubtitlesAsync() {
            throw new NotSupportedException();
        }

        public virtual void GetLatestSubtitlesAsync(string language) {
            throw new NotSupportedException();
        }

        public virtual void GetLatestSubtitlesAsync(string language, uint number) {
            throw new NotSupportedException();
        }

        public event AsyncResponseHandler<IList<Subtitle>> GetLatestSubtitlesEnded {
            add { _getLatestSubtitlesEnded += value; }
            remove { _getLatestSubtitlesEnded -= value; }
        }
        protected AsyncResponseHandler<IList<Subtitle>> _getLatestSubtitlesEnded;
        
        public virtual void GetLatestShowSubtitlesAsync(string showUrl) {
            throw new NotSupportedException();
        }

        public virtual void GetLatestShowSubtitlesAsync(string showUrl, string language) {
            throw new NotSupportedException();
        }

        public virtual void GetLatestShowSubtitlesAsync(string showUrl, string language, uint number) {
            throw new NotSupportedException();
        }

        public event AsyncResponseHandler<IList<Subtitle>> GetLatestShowSubtitlesEnded {
            add { _getLatestShowSubtitlesEnded += value; }
            remove { _getLatestShowSubtitlesEnded -= value; }
        }
        protected AsyncResponseHandler<IList<Subtitle>> _getLatestShowSubtitlesEnded;
        

        public virtual void GetPlanningAsync() {
            throw new NotSupportedException();
        }

        public event AsyncResponseHandler<IList<Episode>> GetPlanningEnded {
            add { _getPlanningEnded += value; }
            remove { _getPlanningEnded -= value; }
        }
        protected AsyncResponseHandler<IList<Episode>> _getPlanningEnded;
        

        public virtual void GetMembersPlanningAsync(string username) {
            throw new NotSupportedException();
        }

        public virtual void GetMembersPlanningAsync(string username, bool unseenOnly) {
            throw new NotSupportedException();
        }

        public event AsyncResponseHandler<IList<Episode>> GetMembersPlanningEnded {
            add { _getMembersPlanningEnded += value; }
            remove { _getMembersPlanningEnded -= value; }
        }
        protected AsyncResponseHandler<IList<Episode>> _getMembersPlanningEnded;
        

        public virtual void AuthenticateAsync(string username, string password) {
            throw new NotSupportedException();
        }

        public event AsyncResponseHandler<string> AuthenticateEnded {
            add { _authenticateEnded += value; }
            remove { _authenticateEnded -= value; }
        }
        protected AsyncResponseHandler<string> _authenticateEnded;

        public virtual void GetIsSessionActiveAsync() {
            throw new NotSupportedException();
        }

        public event AsyncResponseHandler<bool> GetIsSessionActiveEnded {
            add { _getIsSessionActiveEnded += value; }
            remove { _getIsSessionActiveEnded -= value; }
        }
        protected AsyncResponseHandler<bool> _getIsSessionActiveEnded;
        
        public virtual void LogoffAsync() {
            throw new NotSupportedException();
        }

        public event AsyncResponseHandler LogoffEnded {
            add { _logoffEnded += value; }
            remove { _logoffEnded -= value; }
        }
        protected AsyncResponseHandler _logoffEnded;

        public virtual void GetMemberAsync(string username) {
            throw new NotSupportedException();
        }

        public event AsyncResponseHandler<Member> GetMemberEnded {
            add { _getMemberEnded += value; }
            remove { _getMemberEnded -= value; }
        }
        protected AsyncResponseHandler<Member> _getMemberEnded;

        public virtual void GetMembersNextEpisodesAsync(bool onlyNextEpisode) {
            throw new NotSupportedException();
        }

        public event AsyncResponseHandler<IList<Episode>> GetMembersNextEpisodesEnded {
            add { _getMembersNextEpisodesEnded += value; }
            remove { _getMembersNextEpisodesEnded -= value; }
        }
        protected AsyncResponseHandler<IList<Episode>> _getMembersNextEpisodesEnded;

        public virtual void GetMembersNextShowEpisodeAsync(bool onlyNextEpisode, string showUrl) {
            throw new NotSupportedException();
        }

        public event AsyncResponseHandler<Episode> GetMembersNextShowEpisodeEnded {
            add { _getMembersNextShowEpisodeEnded += value; }
            remove { _getMembersNextShowEpisodeEnded -= value; }
        }
        protected AsyncResponseHandler<Episode> _getMembersNextShowEpisodeEnded;
        
        public virtual void SetEpisodeAsSeenAsync(string showUrl, uint season, uint episode, ushort? mark) {
            throw new NotSupportedException();
        }

        public event AsyncResponseHandler SetEpisodeAsSeenEnded {
            add { _setEpisodeAsSeenEnded += value; }
            remove { _setEpisodeAsSeenEnded -= value; }
        }
        protected AsyncResponseHandler _setEpisodeAsSeenEnded;

        public virtual void SetEpisodeAsDownloadedAsync(string showUrl, uint season, uint episode) {
            throw new NotImplementedException();
        }

        public event AsyncResponseHandler SetEpisodeAsDownloadedEnded {
            add { _setEpisodeAsDownloadedEnded += value; }
            remove { _setEpisodeAsDownloadedEnded -= value; }
        }
        protected AsyncResponseHandler _setEpisodeAsDownloadedEnded;

        public virtual void SetEpisodeMarkAsync(string showUrl, uint season, uint episode, ushort mark) {
            throw new NotSupportedException();
        }

        public event AsyncResponseHandler SetEpisodeMarkEnded {
            add { _setEpisodeMarkEnded += value; }
            remove { _setEpisodeMarkEnded -= value; }
        }
        protected AsyncResponseHandler _setEpisodeMarkEnded;
        
        
        public virtual void GetNotificationsAsync(bool? seen, uint? count, uint? fromNotificationId) {
            throw new NotSupportedException();
        }

        public event AsyncResponseHandler<IList<Notification>> GetNotificationsEnded {
            add { _getNotificationsEnded += value; }
            remove { _getNotificationsEnded -= value; }
        }
        protected AsyncResponseHandler<IList<Notification>> _getNotificationsEnded;
        
        public virtual void SignupAsync(string username, string password, string email) {
            throw new NotSupportedException();
        }

        public event AsyncResponseHandler SignupEnded {
            add { _signupEnded += value; }
            remove { _signupEnded -= value; }
        }
        protected AsyncResponseHandler _signupEnded;

        public virtual void GetFriendsAsync(string username) {
            throw new NotSupportedException();
        }

        public event AsyncResponseHandler<IList<string>> GetFriendsEnded {
            add { _getFriendsEnded += value; }
            remove { _getFriendsEnded -= value; }
        }
        protected AsyncResponseHandler<IList<string>> _getFriendsEnded;

        public virtual void GetBadgesAsync(string username) {
            throw new NotSupportedException();
        }

        public event AsyncResponseHandler<string[]> GetBadgesEnded {
            add { _getBadgesEnded += value; }
            remove { _getBadgesEnded -= value; }
        }
        protected AsyncResponseHandler<string[]> _getBadgesEnded;
        
        public virtual void GetCommentsForShowAsync(string showUrl) {
            throw new NotSupportedException();
        }

        public event AsyncResponseHandler<IList<Comment>> GetCommentsForShowEnded {
            add { _getCommentsForShowEnded += value; }
            remove { _getCommentsForShowEnded -= value; }
        }
        protected AsyncResponseHandler<IList<Comment>> _getCommentsForShowEnded;
        
        public virtual void GetCommentsForEpisodeAsync(string showUrl, uint season, uint episode) {
            throw new NotSupportedException();
        }

        public event AsyncResponseHandler<IList<Comment>> GetCommentsForEpisodeEnded {
            add { _getCommentsForEpisodeEnded += value; }
            remove { _getCommentsForEpisodeEnded -= value; }
        }
        protected AsyncResponseHandler<IList<Comment>> _getCommentsForEpisodeEnded;
        
        public virtual void GetCommentsForMemberAsync(string username) {
            throw new NotSupportedException();
        }

        public event AsyncResponseHandler<IList<Comment>> GetCommentsForMemberEnded {
            add { _getCommentsForMemberEnded += value; }
            remove { _getCommentsForMemberEnded -= value; }
        }
        protected AsyncResponseHandler<IList<Comment>> _getCommentsForMemberEnded;
        
        public virtual void CommentShowAsync(string showUrl, string comment, uint? inReplyTo) {
            throw new NotSupportedException();
        }

        public event AsyncResponseHandler CommentShowEnded {
            add { _commentShowEnded += value; }
            remove { _commentShowEnded -= value; }
        }
        protected AsyncResponseHandler _commentShowEnded;
        
        public virtual void CommentEpisodeAsync(string showUrl, uint season, uint episode, string comment, uint? inReplyTo) {
            throw new NotSupportedException();
        }

        public event AsyncResponseHandler CommentEpisodeEnded {
            add { _commentEpisodeEnded += value; }
            remove { _commentEpisodeEnded -= value; }
        }
        protected AsyncResponseHandler _commentEpisodeEnded;
        

        public virtual void CommentMemberAsync(string username, string comment, uint? inReplyTo) {
            throw new NotSupportedException();
        }

        public event AsyncResponseHandler CommentMemberEnded {
            add { _commentMemberEnded += value; }
            remove { _commentMemberEnded -= value; }
        }
        protected AsyncResponseHandler _commentMemberEnded;
        

        public virtual void GetMainTimelineAsync(uint? count) {
            throw new NotSupportedException();
        }

        public event AsyncResponseHandler<IList<TimelineItem>> GetMainTimelineEnded {
            add { _getMainTimelineEnded += value; }
            remove { _getMainTimelineEnded -= value; }
        }
        protected AsyncResponseHandler<IList<TimelineItem>> _getMainTimelineEnded;
        
        public virtual void GetFriendsTimelineAsync(uint? count) {
            throw new NotSupportedException();
        }

        public event AsyncResponseHandler<IList<TimelineItem>> GetFriendsTimelineEnded {
            add { _getFriendsTimelineEnded += value; }
            remove { _getFriendsTimelineEnded -= value; }
        }
        protected AsyncResponseHandler<IList<TimelineItem>> _getFriendsTimelineEnded;
        
        public virtual void GetMemberTimelineAsync(string username, uint? count) {
            throw new NotSupportedException();
        }

        public event AsyncResponseHandler<IList<TimelineItem>> GetMemberTimelineEnded {
            add { _getMemberTimelineEnded += value; }
            remove { _getMemberTimelineEnded -= value; }
        }
        protected AsyncResponseHandler<IList<TimelineItem>> _getMemberTimelineEnded;


        #endregion
#pragma warning restore 1591

        #region Async tools

        #endregion

        #region IDisposable Members

        /// <summary>
        /// Disposing this object when no more needed will prevent memory leaks.
        /// </summary>
        public void Dispose() {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Disposing this object when no more needed will prevent memory leaks.
        /// </summary>
        /// <remarks>
        /// When overriding, don't forget to call the base method and to clean only if disposing is true.
        /// </remarks>
        /// <param name="disposing"></param>
        protected virtual void Dispose(bool disposing) {
            if (disposing) {
                if (factory != null) {
                    factory.SessionTokenChangedEvent -= factory_SessionTokenChangedEvent;
                    factory = null;
                }
                ClearEventHandlers();
            }
        }

        /// <summary>
        /// Set all event handlers to null. 
        /// </summary>
        /// <remarks>
        /// Using reflection is simplier because there are too much event handlers. 
        /// T4 would help.
        /// </remarks>
        private void ClearEventHandlers() {
            BindingFlags flags = BindingFlags.Instance | BindingFlags.Public;
            EventInfo[] events = this.GetType().GetEvents(flags);
            if (events == null)
                return;
            if (events.Length < 1)
                return;

            var ht = new Dictionary<string, FieldInfo>();

            for (int i = 0; i < events.Length; i++) {
                FieldInfo[] fields = events[i].DeclaringType.GetFields(flags);
                foreach (FieldInfo fi in fields) {
                    if (events[i].Name.Equals(fi.Name) && !ht.ContainsKey(fi.Name))
                        ht.Add(fi.Name, fi);
                }
            }

            //TODO: finish that
        }

        #endregion

        /// <summary>
        /// For the authentication method, user passwords must be hashed with MD5.
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        protected static string HashString(string value) {
            byte[] bs = System.Text.Encoding.UTF8.GetBytes(value);
            byte[] hash = null;
            using (MD5Managed md5 = new MD5Managed()) {
                hash = md5.ComputeHash(bs);
            }

            StringBuilder sb = new StringBuilder();
            foreach (byte b in hash) {
                sb.Append(b.ToString("x2").ToLower());
            }

            return sb.ToString();
        }

    }
}
